/* Copyright 2020 Huawei Device Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Author: liangkz @ https://harmonyos.51cto.com/column/46
 * Date  : 2022.04.01
 *
 */
#include "device_resource_if.h"
#include "hdf_device_desc.h"  // HDF 框架对驱动开放相关能力接口的头文件
#include "hdf_log.h"          // HDF 框架提供的日志接口头文件
#include "gpio_if.h"          // drivers/framework/include/platform/gpio_if.h
#include "osal_time.h"        // OsalSleep()

#include "led_drv.h"

#define HDF_LOG_TAG led_drv   // 打印日志所包含的标签，如果不定义则用默认定义的HDF_TAG标签

#define LED_RED_WRITE        1
#define LED_GRN_WRITE        2
#define LED_IFR_WRITE        3   //INFRA_RED
#define LED_BLU_WRITE        3   //BLUE

#define LED_RGB_WRITE        7   //R+G+B tricolour light
#define LED_R_BIT 0x01
#define LED_G_BIT 0x02
#define LED_B_BIT 0x04


#define LED_RED_EVENT_NORMAL 100
#define LED_RED_EVENT_OFF    110
#define LED_RED_EVENT_ON     111

#define LED_GRN_EVENT_NORMAL 200
#define LED_IFR_EVENT_NORMAL 300  //INFRA_RED

struct led_config g_ledcfg = {0};

struct LedMsg {
    int32_t cmd_idx;
    int32_t led_mode;
};

static int32_t LedGpioCtrlExtra(struct HdfDeviceIoClient *client, uint16_t gpio, int32_t mode)  //for LED_RED only
{
	int32_t ret    = HDF_FAILURE;
    int32_t result = HDF_FAILURE;
	struct HdfSBuf *reportBuf = NULL;

    uint16_t sleep = 3;              //default sleep 3s
    uint16_t count = 10;             //default flip 10 times
    uint16_t level = GPIO_VAL_HIGH;  //default to High/ON

    uint16_t ledRedEventId = LED_RED_EVENT_NORMAL;
	const char* ledRedEvent[2] = {
		"LED_RED Extra Toggle OFF",
		"LED_RED Extra Toggle ON",
	};

	if (gpio != g_ledcfg.led_R) {
        HDF_LOGE("LedGpioCtrlExtra: NOT LED_RED, NG");
        return HDF_FAILURE;
	}

    // 将GPIO管脚配置为输出
    if (HDF_SUCCESS != GpioSetDir(gpio, GPIO_DIR_OUT)) {
        HDF_LOGE("LedGpioCtrlExtra: GpioSetDir NG");
        return HDF_FAILURE;
    }

	while (count) {
		GpioRead(gpio, &level);
		level = (level+1)&0x01;

	    if (HDF_SUCCESS != GpioWrite(gpio, level)) {
	        HDF_LOGE("LedGpioCtrlExtra: GpioWrite count[%d] NG",count);
			return HDF_FAILURE;
	    }

		reportBuf = HdfSbufObtainDefaultSize();
		if (reportBuf == NULL) {
			HDF_LOGE("LedGpioCtrlExtra: obtain reportBuf count[%d] NG",count);
			return HDF_FAILURE;
		}

		if (count%3 == 2) {
			ledRedEventId = LED_RED_EVENT_NORMAL;

			//be carefull about the order: a "int32_t" then a "string"
			//write a "int32_t" to reportBuf
			ret = HdfSbufWriteInt32(reportBuf, count);
			//write a "string" to reportBuf
			ret = HdfSbufWriteString(reportBuf, ledRedEvent[level]);
		} else {
			ledRedEventId = LED_RED_EVENT_OFF + level;

			//be carefull about the order: a "string" then a "int32_t"
			//write a "string" to reportBuf
			ret = HdfSbufWriteString(reportBuf, ledRedEvent[level]);
			//write a "int32_t" to reportBuf
			ret = HdfSbufWriteInt32(reportBuf, count);
		}

		//report event to app
		HDF_LOGI("LedGpioCtrlExtra/R: count[%{public}d], HdfDeviceSendEvent(%{public}d) back to app", count, ledRedEventId);
		result = HdfDeviceSendEvent(client->device, ledRedEventId, reportBuf);

		HdfSbufRecycle(reportBuf);
		count--;
		OsalSleep(sleep);
	}

    return result;
}

static int32_t LedGpioCtrl(uint16_t gpio, int32_t mode)
{
    uint16_t level = GPIO_VAL_HIGH;  //default to High/ON

    // 将GPIO管脚配置为输出
    if (HDF_SUCCESS != GpioSetDir(gpio, GPIO_DIR_OUT)) {
        HDF_LOGE("LedGpioCtrl: GpioSetDir NG");
        return HDF_FAILURE;
    }

    // 根据亮灯模式参数配置GPIO管脚输出电平
	if (mode == 1) {                           //1-On
		level = GPIO_VAL_HIGH;
    } else if (mode == 0) {                    //0-Off
		level = GPIO_VAL_LOW;
    } else {//if (mode == -1) {                //-1-flip, default mode
        GpioRead(gpio, &level);
        level = (level+1)&0x01;
    }

    // 改写GPIO管脚输出的电平
    if (HDF_SUCCESS != GpioWrite(gpio, level)) {
        HDF_LOGE("LedGpioCtrl: GpioWrite NG");
        return HDF_FAILURE;
    }
    return HDF_SUCCESS;
}

// Dispatch用来处理用户态发下来的消息
int32_t LedDriverDispatch(struct HdfDeviceIoClient *client, int32_t cmdId, struct HdfSBuf *dataBuf, struct HdfSBuf *replyBuf)
{
    int32_t ret     = HDF_FAILURE;
    int32_t result  = HDF_FAILURE;

    uint32_t readSize = sizeof(struct LedMsg);
    int32_t cmdIdx  = 0;
    int32_t LedMode = -1;

	const char* ledGreenEvent = "LED_GREEN Toggle OK";
	const char* ledRedEvent   = "LED_RED Toggle OK";

    if (client == NULL || client->device == NULL) {
        HDF_LOGE("Led driver device is NULL");
        return HDF_ERR_INVALID_OBJECT;
    }

    if (g_ledcfg.led_ver != 2) {
        HDF_LOGE("Led driver g_ledcfg.led_ver NOT match");
        return HDF_FAILURE;
    }

    switch (cmdId)
	{
    case LED_GRN_WRITE:
		#if 0  //this is OK
		//app write a "struct LedMsg" into dataBuf, so here read a "struct LedMsg"
		struct LedMsg *msg = NULL;
		result = HdfSbufReadBuffer(dataBuf, (const void **)&msg, &readSize);
        if (result) {
			cmdIdx  = msg->cmd_idx;
			LedMode = msg->led_mode;
        }
		#else  //this is OK too
		//app write a "struct LedMsg" into dataBuf, actually it's: '1 uint32_t for size' + '2 int32_t for data',
		//so here read "1 uint32_t + 2 int32_t" will be the same result as reading a "struct LedMsg"
		result = HdfSbufReadUint32(dataBuf, &readSize); //sizeof(LedMsg)
		result = HdfSbufReadInt32(dataBuf, &cmdIdx);
		result = HdfSbufReadInt32(dataBuf, &LedMode);
		#endif

        if (result) {
			HDF_LOGI("LedDriverDispatch[3-1]/G: cmdIdx[%{public}d], LedMode[%{public}d](0-Off/1-On/x-flip)-->>readSize[%{public}d]", cmdIdx, LedMode, readSize);
            result = LedGpioCtrl(g_ledcfg.led_G, LedMode);

			//write a "int32_t" to replyBuf
            ret = HdfSbufWriteInt32(replyBuf, result);
			HDF_LOGI("LedDriverDispatch[3-2]/G: cmdIdx[%{public}d], write result[%{public}d] to replyBuf %{public}s", cmdIdx, result, ret?"OK":"NG");
			//write a "string" to replyBuf
            ret = HdfSbufWriteString(replyBuf, ledGreenEvent);

			#if 1  //report event to app
			struct HdfSBuf *reportBuf = HdfSbufObtainDefaultSize();
			if (reportBuf == NULL) {
				HDF_LOGE("LedDriverDispatch: obtain reportBuf NG");
				return result;
			}
			//write a "int32_t" to reportBuf
			ret = HdfSbufWriteInt32(reportBuf, cmdIdx);
			//write a "string" to reportBuf
			ret = HdfSbufWriteString(reportBuf, ledGreenEvent);

			//report event to app
			HDF_LOGI("LedDriverDispatch[3-3]/G: cmdIdx[%{public}d], HdfDeviceSendEvent(%{public}d) back to app", cmdIdx, LED_GRN_EVENT_NORMAL);
			result = HdfDeviceSendEvent(client->device, LED_GRN_EVENT_NORMAL, reportBuf);

			HdfSbufRecycle(reportBuf);
			#endif

			return result;
        } else {
			HDF_LOGE("LedDriverDispatch: LED_GRN_WRITE: Read dataBuf NG");
        }
        break;

    case LED_RED_WRITE:
		//app write 2 "int32_t" into dataBuf, so here read 2 "int32_t" with the same order
		result = HdfSbufReadInt32(dataBuf, &cmdIdx);
		result = HdfSbufReadInt32(dataBuf, &LedMode);
        if (result) {
			if (LedMode == 9) {  //extra hidden mode
                HDF_LOGI("LedDriverDispatch[1-1]/R: cmdIdx[%{public}d], LedMode[%{public}d](9-extra hidden mode)", cmdIdx, LedMode);
                result = LedGpioCtrlExtra(client, g_ledcfg.led_R, LedMode);

                //reply a "int32_t" back to app
                ret = HdfSbufWriteInt32(replyBuf, result);
                //write a "string" to replyBuf
                ret = HdfSbufWriteString(replyBuf, ledRedEvent);
			} else {
                HDF_LOGI("LedDriverDispatch[3-1]/R: cmdIdx[%{public}d], LedMode[%{public}d](0-Off/1-On/x-flip)", cmdIdx, LedMode);
                result = LedGpioCtrl(g_ledcfg.led_R, LedMode);

                //reply a "int32_t" back to app
                ret = HdfSbufWriteInt32(replyBuf, result);
                HDF_LOGI("LedDriverDispatch[3-2]/R: cmdIdx[%{public}d], write result[%{public}d] to replyBuf %{public}s", cmdIdx, result, ret?"OK":"NG");

                //write a "string" to replyBuf
                ret = HdfSbufWriteString(replyBuf, ledRedEvent);

                //-----------------------------------------------------
                #if 0  //this is OK
                HDF_LOGI("LedDriverDispatch[3-3]/R: cmdIdx[%{public}d], HdfDeviceSendEvent(%{public}d) back to app", result, LED_RED_EVENT_NORMAL);
                result = HdfDeviceSendEvent(client->device, LED_RED_EVENT_NORMAL, replyBuf);
                #else
                //every 3 times report 1 event back to app
                if(cmdIdx%3 == 0) {
                    struct HdfSBuf *reportBuf = HdfSbufObtainDefaultSize();
                    if (reportBuf == NULL) {
                        HDF_LOGE("LedDriverDispatch: obtain reportBuf NG");
                        return result;
                    }
                    //write a "int32_t" to reportBuf
                    ret = HdfSbufWriteInt32(reportBuf, cmdIdx);
                    //write a "string" to reportBuf
                    ret = HdfSbufWriteString(reportBuf, ledRedEvent);

                    //report event to app
                    HDF_LOGI("LedDriverDispatch[3-3]/R: cmdIdx[%{public}d], HdfDeviceSendEvent(%{public}d) back to app", cmdIdx, LED_RED_EVENT_NORMAL);
                    result = HdfDeviceSendEvent(client->device, LED_RED_EVENT_NORMAL, reportBuf);

                    HdfSbufRecycle(reportBuf);
                } else {
                    HDF_LOGI("LedDriverDispatch[3-3]/R: cmdIdx[%{public}d], Do NOT report event back to app", cmdIdx);
                }
                #endif
                //-----------------------------------------------------
			}

			return result;
        } else {
			HDF_LOGE("LedDriverDispatch: LED_RED_WRITE: Read dataBuf NG");
        }
        break;

    case LED_RGB_WRITE:
        result = HdfSbufReadInt32(dataBuf, &LedMode);
        if (result) {
            HDF_LOGI("LedDriverDispatch/RGB: LedMode[0x%{public}X]", LedMode);

            LedGpioCtrl(g_ledcfg.led_R, (LedMode&LED_R_BIT)?1:0);
            LedGpioCtrl(g_ledcfg.led_G, (LedMode&LED_G_BIT)?1:0);
            LedGpioCtrl(g_ledcfg.led_B, (LedMode&LED_B_BIT)?1:0);
        }
        break;

    default:
		HDF_LOGE("LedDriverDispatch: receiving unknown cmdId, NG!!");
        break;
    }
    return result;
}

int32_t LedDriverOpen(struct HdfDeviceIoClient *client)
{
	(void) client;

	HDF_LOGI("LedDriverOpen: OK!!");
    return HDF_SUCCESS;
}

void LedDriverRelease(struct HdfDeviceIoClient *client)
{
	(void) client;

	HDF_LOGI("LedDriverRelease: OK!!");
}

//驱动对外提供的服务能力，将相关的服务接口绑定到HDF框架
int32_t HdfLedDriverBind(struct HdfDeviceObject *deviceObject)
{
    if (deviceObject == NULL) {
        HDF_LOGE("HDF_INIT(g_ledDriverEntry): HdfLedDriverBind: NG!");
        return HDF_ERR_INVALID_OBJECT;
    }

    /**
	 * int32_t (*Dispatch)(struct HdfDeviceIoClient *client, int cmdId, struct HdfSBuf *data, struct HdfSBuf *reply);
     *
     * @brief Called when the driver service is invoked by a user-level application.
     *
     * @param client Indicates the pointer to the client object of the service.
     * @param cmdId  Indicates the command word of the service interface.
     * @param data   Indicates the pointer to the data passed by the invoker.
     * @param reply  Indicates the pointer to the data that needs to be returned to the invoker.
     * @return Returns <b>0</b> if the operation is successful; returns a negative value otherwise.
     */
    static struct IDeviceIoService ledDriverServ = {
        .Open     = LedDriverOpen,     //NOT Necessary
        .Dispatch = LedDriverDispatch, //    Necessary
        .Release  = LedDriverRelease,  //NOT Necessary
    };

    deviceObject->service = (struct IDeviceIoService *)(&ledDriverServ);
    HDF_LOGI("HDF_INIT(g_ledDriverEntry): HdfLedDriverBind: OK! NodeName[%{public}s]", deviceObject->property->name);

    return HDF_SUCCESS;
}

// 驱动自身业务初始的接口
int32_t HdfLedDriverInit(struct HdfDeviceObject *deviceObject)
{
    if (deviceObject == NULL) {
        HDF_LOGE("HDF_INIT(g_ledDriverEntry): HdfLedDriverInit: NG!");
        return HDF_ERR_INVALID_OBJECT;
    }

    struct DeviceResourceIface *CfgOps = NULL;
    CfgOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE);

    if (CfgOps == NULL || CfgOps->GetUint32 == NULL) {
        HDF_LOGE("%s: invalid CfgOps fail!", __func__);
        return HDF_FAILURE;
    }
    if(CfgOps->GetUint32(deviceObject->property, "led_version", &g_ledcfg.led_ver, 0) != HDF_SUCCESS) {
        HDF_LOGE("%s: read led_version fail!", __func__);
        return HDF_FAILURE;
    }
    if(CfgOps->GetUint32(deviceObject->property, "led_R", &g_ledcfg.led_R, 0) != HDF_SUCCESS) {
        g_ledcfg.led_ver = 0;
        HDF_LOGE("%s: read led_R fail!", __func__);
        return HDF_FAILURE;
    }
    if(CfgOps->GetUint32(deviceObject->property, "led_G", &g_ledcfg.led_G, 0) != HDF_SUCCESS) {
        g_ledcfg.led_ver = 0;
        HDF_LOGE("%s: read led_G fail!", __func__);
        return HDF_FAILURE;
    }
    if(CfgOps->GetUint32(deviceObject->property, "led_B", &g_ledcfg.led_B, 0) != HDF_SUCCESS) {
        g_ledcfg.led_ver = 0;
        HDF_LOGE("%s: read led_B fail!", __func__);
        return HDF_FAILURE;
    }

    HDF_LOGI("HDF_INIT(g_ledDriverEntry): HdfLedDriverInit: OK! -->>%{public}s", LED_TEST);  //from led_drv.h
    return HDF_SUCCESS;
}

// 驱动资源释放的接口
void HdfLedDriverRelease(struct HdfDeviceObject *deviceObject)
{
    if (deviceObject == NULL) {
        HDF_LOGE("HDF_INIT(g_ledDriverEntry): HdfLedDriverRelease: NG!");
        return;
    }

    HDF_LOGI("HDF_INIT(g_ledDriverEntry): HdfLedDriverRelease: OK!");
    return;
}

// 定义驱动入口的对象，必须为HdfDriverEntry(在hdf_device_desc.h中定义)类型的全局变量
struct HdfDriverEntry g_ledDriverEntry = {
    .moduleVersion = 1,
    .moduleName = "led_driver",
    .Bind = HdfLedDriverBind,
    .Init = HdfLedDriverInit,
    .Release = HdfLedDriverRelease,
};

// 调用HDF_INIT将驱动入口注册到HDF框架中，在加载驱动时HDF框架会先调用Bind函数,
// 再调用Init函数加载该驱动，当Init调用异常时，HDF框架会调用Release释放驱动资源并退出。
HDF_INIT(g_ledDriverEntry);
